home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / perl5 / Mail / Header.pm < prev    next >
Text File  |  2008-07-29  |  14KB  |  637 lines

  1. # Copyrights 1995-2008 by Mark Overmeer <perl@overmeer.net>.
  2. #  For other contributors see ChangeLog.
  3. # See the manual pages for details on the licensing terms.
  4. # Pod stripped from pm file by OODoc 1.05.
  5. package Mail::Header;
  6. use vars '$VERSION';
  7. $VERSION = '2.04';
  8.  
  9.  
  10. use strict;
  11. use Carp;
  12.  
  13. my $MAIL_FROM = 'KEEP';
  14. my %HDR_LENGTHS = ();
  15.  
  16. # Pattern to match a RFC822 Field name ( Extract from RFC #822)
  17. #
  18. #     field       =  field-name ":" [ field-body ] CRLF
  19. #
  20. #     field-name  =  1*<any CHAR, excluding CTLs, SPACE, and ":">
  21. #
  22. #     CHAR        =  <any ASCII character>        ; (  0-177,  0.-127.)
  23. #     CTL         =  <any ASCII control           ; (  0- 37,  0.- 31.)
  24. #              character and DEL>          ; (    177,     127.)
  25. # I have included the trailing ':' in the field-name
  26. #
  27. our $FIELD_NAME = '[^\x00-\x1f\x7f-\xff :]+:';
  28.  
  29.  
  30. ##
  31. ## Private functions
  32. ##
  33.  
  34. sub _error { warn @_; () }
  35.  
  36. # tidy up internal hash table and list
  37.  
  38. sub _tidy_header
  39. {   my $self    = shift;
  40.     my $deleted = 0;
  41.  
  42.     for(my $i = 0 ; $i < @{$self->{mail_hdr_list}}; $i++)
  43.     {   next if defined $self->{mail_hdr_list}[$i];
  44.  
  45.         splice @{$self->{mail_hdr_list}}, $i, 1;
  46.         $deleted++;
  47.         $i--;
  48.     }
  49.  
  50.     if($deleted)
  51.     {   local $_;
  52.         my @del;
  53.  
  54.         while(my ($key,$ref) = each %{$self->{mail_hdr_hash}} )
  55.         {   push @del, $key
  56.            unless @$ref = grep { ref $_ && defined $$_ } @$ref;
  57.         }
  58.  
  59.         delete $self->{'mail_hdr_hash'}{$_} for @del;
  60.     }
  61. }
  62.  
  63. # fold the line to the given length
  64.  
  65. my %STRUCTURE = map { (lc $_ => undef) }
  66.   qw{ To Cc Bcc From Date Reply-To Sender
  67.       Resent-Date Resent-From Resent-Sender Resent-To Return-Path
  68.       list-help list-post list-unsubscribe Mailing-List
  69.       Received References Message-ID In-Reply-To
  70.       Content-Length Content-Type Content-Disposition
  71.       Delivered-To
  72.       Lines
  73.       MIME-Version
  74.       Precedence
  75.       Status
  76.     };
  77.  
  78. sub _fold_line
  79. {   my($ln,$maxlen) = @_;
  80.  
  81.     $maxlen = 20
  82.        if $maxlen < 20;
  83.  
  84.     my $max = int($maxlen - 5);         # 4 for leading spcs + 1 for [\,\;]
  85.     my $min = int($maxlen * 4 / 5) - 4;
  86.  
  87.     $_[0] =~ s/[\r\n]+//og;        # Remove new-lines
  88.     $_[0] =~ s/\s*\Z/\n/so;        # End line with a EOLN
  89.  
  90.     return if $_[0] =~ /^From\s/io;
  91.  
  92.     if(length($_[0]) > $maxlen)
  93.     {   if($_[0] =~ /^([-\w]+)/ && exists $STRUCTURE{ lc $1 } )
  94.         {   #Split the line up
  95.             # first bias towards splitting at a , or a ; >4/5 along the line
  96.             # next split a whitespace
  97.             # else we are looking at a single word and probably don't want to split
  98.             my $x = "";
  99.             $x .= "$1\n " while $_[0] =~
  100.                 s/^\s*
  101.                    ( [^"]{$min,$max} [,;]
  102.                    | [^"]{1,$max}    [,;\s]
  103.                    | [^\s"]*(?:"[^"]*"[ \t]?[^\s"]*)+\s
  104.                    ) //x;
  105.  
  106.             $x .= $_[0];
  107.             $_[0] = $x;
  108.             $_[0] =~ s/(\A\s+|[\t ]+\Z)//sog;
  109.             $_[0] =~ s/\s+\n/\n/sog;
  110.         }
  111.         else
  112.         {   $_[0] =~ s/(.{$min,$max})(\s)/$1\n$2/g;
  113.             $_[0] =~ s/\s*$/\n/s;
  114.         }
  115.     }
  116.  
  117.     $_[0] =~ s/\A(\S+)\n\s*(?=\S)/$1 /so; 
  118. }
  119.  
  120. # Tags are case-insensitive, but there is a (slightly) prefered construction
  121. # being all characters are lowercase except the first of each word. Also
  122. # if the word is an `acronym' then all characters are uppercase. We decide
  123. # a word is an acronym if it does not contain a vowel.
  124. # In general, this change of capitization is a bad idea, but it is in
  125. # the code for ages, and therefore probably crucial for existing
  126. # applications.
  127.  
  128. sub _tag_case
  129. {   my $tag = shift;
  130.     $tag =~ s/\:$//;
  131.     join '-'
  132.       , map { /^[b-df-hj-np-tv-z]+$|^(?:MIME|SWE|SOAP|LDAP|ID)$/i
  133.               ? uc($_) : ucfirst(lc($_))
  134.             } split m/\-/, $tag, -1;
  135. }
  136.  
  137. # format a complete line
  138. #  ensure line starts with the given tag
  139. #  ensure tag is correct case
  140. #  change the 'From ' tag as required
  141. #  fold the line
  142.  
  143. sub _fmt_line
  144. {   my ($self, $tag, $line, $modify) = @_;
  145.     $modify ||= $self->{mail_hdr_modify};
  146.     my $ctag = undef;
  147.  
  148.     ($tag) = $line =~ /^($FIELD_NAME|From )/oi
  149.         unless defined $tag;
  150.  
  151.     if(defined $tag && $tag =~ /^From /io && $self->{mail_hdr_mail_from} ne 'KEEP')
  152.     {   if($self->{mail_hdr_mail_from} eq 'COERCE')
  153.         {   $line =~ s/^From /Mail-From: /o;
  154.             $tag = "Mail-From:";
  155.         }
  156.         elsif($self->{mail_hdr_mail_from} eq 'IGNORE')
  157.         {   return ();
  158.         }
  159.         elsif($self->{mail_hdr_mail_from} eq 'ERROR')
  160.         {    return _error "unadorned 'From ' ignored: <$line>";
  161.         }
  162.     }
  163.  
  164.     if(defined $tag)
  165.     {   $tag  = _tag_case($ctag = $tag);
  166.         $ctag = $tag if $modify;
  167.         $ctag =~ s/([^ :])$/$1:/o if defined $ctag;
  168.     }
  169.  
  170.     defined $ctag && $ctag =~ /^($FIELD_NAME|From )/oi
  171.         or croak "Bad RFC822 field name '$tag'\n";
  172.  
  173.     # Ensure the line starts with tag
  174.     if(defined $ctag && ($modify || $line !~ /^\Q$ctag\E/i))
  175.     {   (my $xtag = $ctag) =~ s/\s*\Z//o;
  176.         $line =~ s/^(\Q$ctag\E)?\s*/$xtag /i;
  177.     }
  178.  
  179.     my $maxlen = $self->{mail_hdr_lengths}{$tag}
  180.               || $HDR_LENGTHS{$tag}
  181.               || $self->fold_length;
  182.  
  183.     _fold_line $line, $maxlen
  184.         if $modify && defined $maxlen;
  185.  
  186.     $line =~ s/\n*$/\n/so;
  187.     ($tag, $line);
  188. }
  189.  
  190. sub _insert
  191. {   my ($self, $tag, $line, $where) = @_;
  192.  
  193.     if($where < 0)
  194.     {   $where = @{$self->{mail_hdr_list}} + $where + 1;
  195.         $where = 0 if $where < 0;
  196.     }
  197.     elsif($where >= @{$self->{mail_hdr_list}})
  198.     {   $where = @{$self->{mail_hdr_list}};
  199.     }
  200.  
  201.     my $atend = $where == @{$self->{mail_hdr_list}};
  202.     splice @{$self->{mail_hdr_list}}, $where, 0, $line;
  203.  
  204.     $self->{mail_hdr_hash}{$tag} ||= [];
  205.     my $ref = \${$self->{mail_hdr_list}}[$where];
  206.  
  207.     my $def = $self->{mail_hdr_hash}{$tag};
  208.     if($def && $where)
  209.     {   if($atend) { push @$def, $ref }
  210.         else
  211.         {   my $i = 0;
  212.             foreach my $ln (@{$self->{mail_hdr_list}})
  213.             {   my $r = \$ln;
  214.                 last if $r == $ref;
  215.                 $i++ if $r == $def->[$i];
  216.             }
  217.             splice @$def, $i, 0, $ref;
  218.         }
  219.     }
  220.     else
  221.     {    unshift @$def, $ref;
  222.     }
  223. }
  224.  
  225.  
  226. sub new
  227. {   my $call  = shift;
  228.     my $class = ref($call) || $call;
  229.     my $arg   = @_ % 2 ? shift : undef;
  230.     my %opt   = @_;
  231.  
  232.     $opt{Modify} = delete $opt{Reformat}
  233.         unless exists $opt{Modify};
  234.  
  235.     my $self = bless
  236.       { mail_hdr_list     => []
  237.       , mail_hdr_hash     => {}
  238.       , mail_hdr_modify   => (delete $opt{Modify} || 0)
  239.       , mail_hdr_foldlen  => 79
  240.       , mail_hdr_lengths  => {}
  241.       }, $class;
  242.  
  243.     $self->mail_from( uc($opt{MailFrom} || $MAIL_FROM) );
  244.  
  245.     $self->fold_length($opt{FoldLength})
  246.         if exists $opt{FoldLength};
  247.  
  248.     if(!ref $arg)               {}
  249.     elsif(ref($arg) eq 'ARRAY') { $self->extract( [ @$arg ] ) }
  250.     elsif(defined fileno($arg)) { $self->read($arg) }
  251.  
  252.     $self;
  253. }
  254.  
  255.  
  256. sub dup
  257. {   my $self = shift;
  258.     my $dup  = ref($self)->new;
  259.  
  260.     %$dup    = %$self;
  261.     $dup->empty;      # rebuild tables
  262.  
  263.     $dup->{mail_hdr_list} = [ @{$self->{mail_hdr_list}} ];
  264.  
  265.     foreach my $ln ( @{$dup->{mail_hdr_list}} )
  266.     {    my $tag = _tag_case +($ln =~ /^($FIELD_NAME|From )/oi)[0];
  267.          push @{$dup->{mail_hdr_hash}{$tag}}, \$ln;
  268.     }
  269.  
  270.     $dup;
  271. }
  272.  
  273.  
  274. sub extract
  275. {   my ($self, $lines) = @_;
  276.     $self->empty;
  277.  
  278.     while(@$lines && $lines->[0] =~ /^($FIELD_NAME|From )/o)
  279.     {    my $tag  = $1;
  280.          my $line = shift @$lines;
  281.          $line   .= shift @$lines
  282.              while @$lines && $lines->[0] =~ /^[ \t]+/o;
  283.  
  284.          ($tag, $line) = _fmt_line $self, $tag, $line;
  285.  
  286.          _insert $self, $tag, $line, -1
  287.              if defined $line;
  288.     }
  289.  
  290.     shift @$lines
  291.         if @$lines && $lines->[0] =~ /^\s*$/o;
  292.  
  293.     $self;
  294. }
  295.  
  296.  
  297. sub read
  298. {   my ($self, $fd) = @_;
  299.  
  300.     $self->empty;
  301.  
  302.     my ($tag, $line);
  303.     my $ln = '';
  304.     while(1)
  305.     {   $ln = <$fd>;
  306.  
  307.         if(defined $ln && defined $line && $ln =~ /\A[ \t]+/o)
  308.         {   $line .= $ln;
  309.             next;
  310.         }
  311.  
  312.         if(defined $line)
  313.         {   ($tag, $line) = _fmt_line $self, $tag, $line;
  314.             _insert $self, $tag, $line, -1
  315.             if defined $line;
  316.         }
  317.  
  318.         defined $ln && $ln =~ /^($FIELD_NAME|From )/o
  319.             or last;
  320.  
  321.         ($tag, $line) = ($1, $ln);
  322.     }
  323.  
  324.     $self;
  325. }
  326.  
  327.  
  328. sub empty
  329. {   my $self = shift;
  330.     $self->{mail_hdr_list} = [];
  331.     $self->{mail_hdr_hash} = {};
  332.     $self;
  333. }
  334.  
  335.  
  336. sub header
  337. {   my $self = shift;
  338.  
  339.     $self->extract(@_)
  340.     if @_;
  341.  
  342.     $self->fold
  343.         if $self->{mail_hdr_modify};
  344.  
  345.     [ @{$self->{mail_hdr_list}} ];
  346. }
  347.  
  348.  
  349. ### text kept, for educational purpose... originates from 2000/03
  350. # This can probably be optimized. I didn't want to mess much around with
  351. # the internal implementation as for now...
  352. # -- Tobias Brox <tobix@cpan.org>
  353.  
  354. sub header_hashref
  355. {   my ($self, $hashref) = @_;
  356.  
  357.     while(my ($key, $value) = each %$hashref)
  358.     {   $self->add($key, $_) for ref $value ? @$value : $value;
  359.     }
  360.  
  361.     $self->fold
  362.         if $self->{mail_hdr_modify};
  363.  
  364.     defined wantarray  # MO, added minimal optimization
  365.         or return;
  366.  
  367.     +{ map { ($_ => [$self->get($_)] ) }   # MO: Eh?
  368.            keys %{$self->{mail_hdr_hash}}
  369.      }; 
  370. }
  371.  
  372.  
  373. sub modify
  374. {   my $self = shift;
  375.     my $old  = $self->{mail_hdr_modify};
  376.  
  377.     $self->{mail_hdr_modify} = 0 + shift
  378.     if @_;
  379.  
  380.     $old;
  381. }
  382.  
  383.  
  384. sub mail_from
  385. {   my $thing  = shift;
  386.     my $choice = uc shift;
  387.  
  388.     $choice =~ /^(IGNORE|ERROR|COERCE|KEEP)$/ 
  389.     or die "bad Mail-From choice: '$choice'";
  390.  
  391.     if(ref $thing) { $thing->{mail_hdr_mail_from} = $choice }
  392.     else           { $MAIL_FROM = $choice }
  393.  
  394.     $thing;
  395. }
  396.  
  397.  
  398. sub fold_length
  399. {   my $thing = shift;
  400.     my $old;
  401.  
  402.     if(@_ == 2)
  403.     {   my $tag = _tag_case shift;
  404.         my $len = shift;
  405.  
  406.         my $hash = ref $thing ? $thing->{mail_hdr_lengths} : \%HDR_LENGTHS;
  407.         $old     = $hash->{$tag};
  408.         $hash->{$tag} = $len > 20 ? $len : 20;
  409.     }
  410.     else
  411.     {   my $self = $thing;
  412.         my $len  = shift;
  413.         $old = $self->{mail_hdr_foldlen};
  414.  
  415.         if(defined $len)
  416.         {    $self->{mail_hdr_foldlen} = $len > 20 ? $len : 20;
  417.              $self->fold if $self->{mail_hdr_modify};
  418.         }
  419.     }
  420.  
  421.     $old;
  422. }
  423.  
  424.  
  425. sub fold
  426. {   my ($self, $maxlen) = @_;
  427.  
  428.     while(my ($tag, $list) = each %{$self->{mail_hdr_hash}})
  429.     {   my $len = $maxlen
  430.              || $self->{mail_hdr_lengths}{$tag}
  431.              || $HDR_LENGTHS{$tag}
  432.              || $self->fold_length;
  433.  
  434.         foreach my $ln (@$list)
  435.         {    _fold_line $$ln, $len
  436.                  if defined $ln;
  437.         }
  438.     }
  439.  
  440.     $self;
  441. }
  442.  
  443.  
  444. sub unfold
  445. {   my $self = shift;
  446.  
  447.     if(@_)
  448.     {   my $tag  = _tag_case shift;
  449.         my $list = $self->{mail_hdr_hash}{$tag}
  450.             or return $self;
  451.  
  452.         foreach my $ln (@$list)
  453.         {   $$ln =~ s/\r?\n\s+/ /sog
  454.                 if defined $ln && defined $$ln;
  455.         }
  456.  
  457.         return $self;
  458.     }
  459.  
  460.     while( my ($tag, $list) = each %{$self->{mail_hdr_hash}})
  461.     {   foreach my $ln (@$list)
  462.         {   $$ln =~ s/\r?\n\s+/ /sog
  463.             if defined $ln && defined $$ln;
  464.         }
  465.     }
  466.  
  467.     $self;
  468. }
  469.  
  470.  
  471. sub add
  472. {   my ($self, $tag, $text, $where) = @_;
  473.     ($tag, my $line) = _fmt_line $self, $tag, $text;
  474.  
  475.     defined $tag && defined $line
  476.         or return undef;
  477.  
  478.     defined $where
  479.         or $where = -1;
  480.  
  481.     _insert $self, $tag, $line, $where;
  482.  
  483.     $line =~ /^\S+\s(.*)/os;
  484.     $1;
  485. }
  486.  
  487.  
  488. sub replace
  489. {   my $self = shift;
  490.     my $idx  = @_ % 2 ? pop @_ : 0;
  491.  
  492.     my ($tag, $line);
  493.   TAG:
  494.     while(@_)
  495.     {   ($tag,$line) = _fmt_line $self, splice(@_,0,2);
  496.  
  497.         defined $tag && defined $line
  498.             or return undef;
  499.  
  500.         my $field = $self->{mail_hdr_hash}{$tag};
  501.         if($field && defined $field->[$idx])
  502.              { ${$field->[$idx]} = $line }
  503.         else { _insert $self, $tag, $line, -1 }
  504.     }
  505.  
  506.     $line =~ /^\S+\s*(.*)/os;
  507.     $1;
  508. }
  509.  
  510.  
  511. sub combine
  512. {   my $self = shift;
  513.     my $tag  = _tag_case shift;
  514.     my $with = shift || ' ';
  515.  
  516.     $tag =~ /^From /io && $self->{mail_hdr_mail_from} ne 'KEEP'
  517.         and return _error "unadorned 'From ' ignored";
  518.  
  519.     my $def = $self->{mail_hdr_hash}{$tag}
  520.         or return undef;
  521.  
  522.     return $def->[0]
  523.         if @$def <= 1;
  524.  
  525.     my @lines = $self->get($tag);
  526.     chomp @lines;
  527.  
  528.     my $line = (_fmt_line $self, $tag, join($with,@lines), 1)[1];
  529.  
  530.     $self->{mail_hdr_hash}{$tag} = [ \$line ];
  531.     $line;
  532. }
  533.  
  534.  
  535. sub get
  536. {   my $self = shift;
  537.     my $tag = _tag_case shift;
  538.     my $idx = shift;
  539.  
  540.     my $def = $self->{mail_hdr_hash}{$tag}
  541.         or return ();
  542.  
  543.     my $l = length $tag;
  544.     $l   += 1 if $tag !~ / $/o;
  545.  
  546.     if(defined $idx || !wantarray)
  547.     {    $idx ||= 0;
  548.          my $val = ${$def->[$idx]};
  549.          defined $val or return undef;
  550.  
  551.      $val = substr $val, $l;
  552.      $val =~ s/^\s+//;
  553.          return $val;
  554.     }
  555.  
  556.     map { my $tmp = substr $$_,$l; $tmp =~ s/^\s+//; $tmp } @$def;
  557. }
  558.  
  559.  
  560.  
  561. sub count
  562. {   my $self = shift;
  563.     my $tag  = _tag_case shift;
  564.     my $def  = $self->{mail_hdr_hash}{$tag};
  565.     defined $def ? scalar(@$def) : 0;
  566. }
  567.  
  568.  
  569.  
  570. sub delete
  571. {   my $self = shift;
  572.     my $tag  = _tag_case shift;
  573.     my $idx  = shift;
  574.     my @val;
  575.  
  576.     if(my $def = $self->{mail_hdr_hash}{$tag})
  577.     {   my $l = length $tag;
  578.         $l   += 2 if $tag !~ / $/;
  579.  
  580.         if(defined $idx)
  581.         {   if(defined $def->[$idx])
  582.             {   push @val, substr ${$def->[$idx]}, $l;
  583.                 undef ${$def->[$idx]};
  584.             }
  585.         }
  586.         else
  587.         {   @val = map {my $x = substr $$_,$l; undef $$_; $x } @$def;
  588.         }
  589.  
  590.         _tidy_header($self);
  591.     }
  592.  
  593.     @val;
  594. }
  595.  
  596.  
  597.  
  598. sub print
  599. {   my $self = shift;
  600.     my $fd   = shift || \*STDOUT;
  601.  
  602.     foreach my $ln (@{$self->{mail_hdr_list}})
  603.     {   defined $ln or next;
  604.         print $fd $ln or return 0;
  605.     }
  606.  
  607.     1;
  608. }
  609.  
  610.  
  611. sub as_string { join '', grep {defined} @{shift->{mail_hdr_list}} }
  612.  
  613.  
  614. sub tags { keys %{shift->{mail_hdr_hash}} }
  615.  
  616.  
  617. sub cleanup
  618. {   my $self = shift;
  619.     my $deleted = 0;
  620.  
  621.     foreach my $key (@_ ? @_ : keys %{$self->{mail_hdr_hash}})
  622.     {   my $fields = $self->{mail_hdr_hash}{$key};
  623.         foreach my $field (@$fields)
  624.         {   next if $$field =~ /^\S+\s+\S/s;
  625.             undef $$field;
  626.             $deleted++;
  627.         }
  628.     }
  629.  
  630.     _tidy_header $self
  631.         if $deleted;
  632.  
  633.     $self;  
  634. }
  635.  
  636. 1;
  637.